home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / grid.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-24  |  35.8 KB  |  1,112 lines

  1. /* The GIMP -- an image manipulation program 
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis 
  3.  * 
  4.  * This program is free software; you can redistribute it and/or modify 
  5.  * it under the terms of the GNU General Public License as published by 
  6.  * the Free Software Foundation; either version 2 of the License, or 
  7.  * (at your option) any later version. 
  8.  *
  9.  * This program is distributed in the hope that it will be useful, 
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12.  * GNU General Public License for more details. 
  13.  * 
  14.  * You should have received a copy of the GNU General Public License 
  15.  * along with this program; if not, write to the Free Software 
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
  17.  */
  18.  
  19. /* Original plug-in coded by Tim Newsome. 
  20.  * 
  21.  * Changed to make use of real-life units by Sven Neumann <sven@gimp.org>.
  22.  * 
  23.  * The interface code is heavily commented in the hope that it will
  24.  * help other plug-in developers to adapt their plug-ins to make use
  25.  * of the gimp_size_entry functionality. 
  26.  * 
  27.  * Note: There is a convenience constructor called gimp_coordinetes_new ()
  28.  *       which simplifies the task of setting up a standard X,Y sizeentry. 
  29.  *
  30.  * For more info and bugs see libgimp/gimpsizeentry.h and libgimp/gimpwidgets.h
  31.  *
  32.  * May 2000 tim copperfield [timecop@japan.co.jp]
  33.  * http://www.ne.jp/asahi/linux/timecop
  34.  * Added dynamic preview.  Due to weird implementation of signals from all
  35.  * controls, preview will not auto-update.  But this plugin isn't really
  36.  * crying for real-time updating either.
  37.  *
  38.  */
  39.  
  40. #include "config.h"
  41.  
  42. #include <stdlib.h>
  43. #include <stdio.h>
  44. #include <string.h>
  45.  
  46. #include <gtk/gtk.h>
  47.  
  48. #include <libgimp/gimp.h>
  49. #include <libgimp/gimpui.h>
  50.  
  51. #include "libgimp/stdplugins-intl.h"
  52.  
  53.  
  54. #define  SPIN_BUTTON_WIDTH   75
  55. #define  COLOR_BUTTON_WIDTH  55
  56. #define  PREVIEW_SIZE        128
  57.  
  58. /* Declare local functions. */
  59. static void   query  (void);
  60. static void   run    (gchar      *name,
  61.               gint        nparams,
  62.               GimpParam  *param,
  63.               gint       *nreturn_vals,
  64.               GimpParam **return_vals);
  65.  
  66. static guchar      best_cmap_match (guchar       *cmap, 
  67.                     gint          ncolors,
  68.                     guchar       *color);
  69. static void        doit            (gint32        image_ID, 
  70.                     GimpDrawable *drawable, 
  71.                     gboolean      preview_mode);
  72. static gint        dialog          (gint32        image_ID, 
  73.                     GimpDrawable *drawable);
  74. static GtkWidget * preview_widget  (gint32        image_ID,
  75.                                     GimpDrawable *drawable);
  76. static void        fill_preview    (GtkWidget    *preview_widget, 
  77.                     gint32        image_ID,
  78.                                     GimpDrawable *drawable);
  79.  
  80. GimpPlugInInfo PLUG_IN_INFO =
  81. {
  82.   NULL,  /* init_proc  */
  83.   NULL,  /* quit_proc  */
  84.   query, /* query_proc */
  85.   run,   /* run_proc   */
  86. };
  87.  
  88. gint sx1, sy1, sx2, sy2;
  89. gint run_flag = FALSE;
  90.  
  91. static GtkWidget *hcolor_button;
  92. static GtkWidget *vcolor_button;
  93. static guchar    *preview_bits;
  94. static GtkWidget *preview;
  95.  
  96.  
  97. typedef struct
  98. {
  99.   gint   hwidth;
  100.   gint   hspace;
  101.   gint   hoffset;
  102.   guint8 hcolor[4];
  103.   gint   vwidth;
  104.   gint   vspace;
  105.   gint   voffset;
  106.   guint8 vcolor[4];
  107.   gint   iwidth;
  108.   gint   ispace;
  109.   gint   ioffset;
  110.   guint8 icolor[4];
  111. }
  112. Config;
  113.  
  114. Config grid_cfg =
  115. {
  116.   1, 16, 8, { 0, 0, 128, 255 },    /* horizontal   */
  117.   1, 16, 8, { 0, 0, 128, 255 },    /* vertical     */
  118.   0,  2, 6, { 0, 0, 255, 255 },    /* intersection */
  119. };
  120.  
  121.  
  122. MAIN ()
  123.  
  124. static 
  125. void query (void)
  126. {
  127.   static GimpParamDef args[] =
  128.   {
  129.     { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
  130.     { GIMP_PDB_IMAGE,    "image",    "Input image" },
  131.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  132.  
  133.     { GIMP_PDB_INT32,    "hwidth",   "Horizontal Width   (>= 0)" },
  134.     { GIMP_PDB_INT32,    "hspace",   "Horizontal Spacing (>= 1)" },
  135.     { GIMP_PDB_INT32,    "hoffset",  "Horizontal Offset  (>= 0)" },
  136.     { GIMP_PDB_COLOR,    "hcolor",   "Horizontal Colour" },
  137.     { GIMP_PDB_INT8,     "hopacity", "Horizontal Opacity (0...255)" },
  138.  
  139.     { GIMP_PDB_INT32,    "vwidth",   "Vertical Width   (>= 0)" },
  140.     { GIMP_PDB_INT32,    "vspace",   "Vertical Spacing (>= 1)" },
  141.     { GIMP_PDB_INT32,    "voffset",  "Vertical Offset  (>= 0)" },
  142.     { GIMP_PDB_COLOR,    "vcolor",   "Vertical Colour" },
  143.     { GIMP_PDB_INT8,     "vopacity", "Vertical Opacity (0...255)" },
  144.  
  145.     { GIMP_PDB_INT32,    "iwidth",   "Intersection Width   (>= 0)" },
  146.     { GIMP_PDB_INT32,    "ispace",   "Intersection Spacing (>= 0)" },
  147.     { GIMP_PDB_INT32,    "ioffset",  "Intersection Offset  (>= 0)" },
  148.     { GIMP_PDB_COLOR,    "icolor",   "Intersection Colour" },
  149.     { GIMP_PDB_INT8,     "iopacity", "Intersection Opacity (0...255)" }
  150.   };
  151.   static gint nargs = sizeof (args) / sizeof (args[0]);
  152.  
  153.   gimp_install_procedure ("plug_in_grid",
  154.               "Draws a grid.",
  155.               "Draws a grid using the specified colors. "
  156.               "The grid origin is the upper left corner.",
  157.               "Tim Newsome",
  158.               "Tim Newsome, Sven Neumann, Tom Rathborne, TC",
  159.               "1997 - 2000",
  160.               N_("<Image>/Filters/Render/Pattern/Grid..."),
  161.               "RGB*, GRAY*, INDEXED*",
  162.               GIMP_PLUGIN,
  163.               nargs, 0,
  164.               args, NULL);
  165. }
  166.  
  167. static void
  168. run (gchar      *name, 
  169.      gint        n_params, 
  170.      GimpParam  *param, 
  171.      gint       *nreturn_vals,
  172.      GimpParam **return_vals)
  173. {
  174.   static GimpParam values[1];
  175.   GimpDrawable *drawable;
  176.   gint32 image_ID;
  177.   GimpRunModeType run_mode;
  178.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  179.  
  180.   *nreturn_vals = 1;
  181.   *return_vals = values;
  182.  
  183.   INIT_I18N_UI(); 
  184.  
  185.   run_mode = param[0].data.d_int32;
  186.   image_ID = param[1].data.d_int32;
  187.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  188.  
  189.   if (run_mode == GIMP_RUN_NONINTERACTIVE)
  190.     {
  191.       if (n_params != 18)
  192.     status = GIMP_PDB_CALLING_ERROR;
  193.  
  194.       if (status == GIMP_PDB_SUCCESS)
  195.     {
  196.       grid_cfg.hwidth    = MAX (0, param[3].data.d_int32);
  197.       grid_cfg.hspace    = MAX (1, param[4].data.d_int32);
  198.       grid_cfg.hoffset   = MAX (0, param[5].data.d_int32);
  199.       grid_cfg.hcolor[0] = param[6].data.d_color.red;
  200.       grid_cfg.hcolor[1] = param[6].data.d_color.green;
  201.       grid_cfg.hcolor[2] = param[6].data.d_color.blue;
  202.       grid_cfg.hcolor[3] = param[7].data.d_int8;
  203.  
  204.       grid_cfg.vwidth    = MAX (0, param[8].data.d_int32);
  205.       grid_cfg.vspace    = MAX (1, param[9].data.d_int32);
  206.       grid_cfg.voffset   = MAX (0, param[10].data.d_int32);
  207.       grid_cfg.vcolor[0] = param[11].data.d_color.red;
  208.       grid_cfg.vcolor[1] = param[11].data.d_color.green;
  209.       grid_cfg.vcolor[2] = param[11].data.d_color.blue;
  210.       grid_cfg.vcolor[3] = param[12].data.d_int8;
  211.  
  212.       grid_cfg.iwidth    = MAX (0, param[13].data.d_int32);
  213.       grid_cfg.ispace    = MAX (0, param[14].data.d_int32);
  214.       grid_cfg.ioffset   = MAX (0, param[15].data.d_int32);
  215.       grid_cfg.icolor[0] = param[16].data.d_color.red;
  216.       grid_cfg.icolor[1] = param[16].data.d_color.green;
  217.       grid_cfg.icolor[2] = param[16].data.d_color.blue;
  218.       grid_cfg.icolor[3] = param[17].data.d_int8;
  219.     }
  220.     }
  221.   else
  222.     {
  223.       /*  Possibly retrieve data  */
  224.       gimp_get_data ("plug_in_grid", &grid_cfg);
  225.     }
  226.  
  227.   if (run_mode == GIMP_RUN_INTERACTIVE)
  228.     {
  229.       if (!dialog (image_ID, drawable))
  230.     {
  231.       /* The dialog was closed, or something similarly evil happened. */
  232.       status = GIMP_PDB_EXECUTION_ERROR;
  233.     }
  234.       g_free(preview_bits);
  235.     }
  236.  
  237.   if (grid_cfg.hspace <= 0 || grid_cfg.vspace <= 0)
  238.     {
  239.       status = GIMP_PDB_EXECUTION_ERROR;
  240.     }
  241.  
  242.   if (status == GIMP_PDB_SUCCESS)
  243.     {
  244.       gimp_progress_init (_("Drawing Grid..."));
  245.       gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  246.       
  247.       doit (image_ID, drawable, FALSE);
  248.       
  249.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  250.     gimp_displays_flush ();
  251.       
  252.       if (run_mode == GIMP_RUN_INTERACTIVE)
  253.     gimp_set_data ("plug_in_grid", &grid_cfg, sizeof (grid_cfg));
  254.  
  255.       gimp_drawable_detach (drawable);
  256.     }
  257.  
  258.   values[0].type = GIMP_PDB_STATUS;
  259.   values[0].data.d_status = status;
  260. }
  261.  
  262.  
  263. #define MAXDIFF 195076
  264.  
  265. static guchar
  266. best_cmap_match (guchar *cmap,
  267.          gint    ncolors,
  268.          guchar *color)
  269. {
  270.   guchar cmap_index = 0;
  271.   gint max = MAXDIFF;
  272.   gint i, diff, sum;
  273.  
  274.   for (i = 0; i < ncolors; i++)
  275.     {
  276.       diff = color[0] - *cmap++;
  277.       sum = SQR (diff);
  278.       diff = color[1] - *cmap++;
  279.       sum += SQR (diff);
  280.       diff = color[2] - *cmap++;
  281.       sum += SQR (diff);
  282.       
  283.       if (sum < max)
  284.     {
  285.       cmap_index = i;
  286.       max = sum;
  287.     }
  288.     }
  289.  
  290.   return cmap_index;
  291. }
  292.  
  293.  
  294. G_INLINE_FUNC void
  295. pix_composite (guchar   *p1, 
  296.            guchar    p2[4], 
  297.            gint      bytes,
  298.            gboolean  blend,
  299.            gboolean  alpha)
  300. {
  301.   gint b;
  302.  
  303.   if (alpha)
  304.     {
  305.       bytes--;
  306.     }
  307.   
  308.   if (blend)
  309.     {
  310.       for (b = 0; b < bytes; b++)
  311.     {
  312.       *p1 = *p1 * (1.0 - p2[3]/255.0) + p2[b] * p2[3]/255.0;
  313.       p1++;
  314.     }
  315.     }
  316.   else
  317.     {
  318.       /* blend should only be TRUE for indexed (bytes == 1) */
  319.       *p1++ = *p2;
  320.     }
  321.  
  322.   if (alpha && *p1 < 255)
  323.     {
  324.       b = *p1 + 255.0 * ((double)p2[3] / (255.0 - *p1));
  325.       *p1 = b > 255 ? 255 : b;
  326.     }
  327. }
  328.  
  329.  
  330. static void
  331. doit (gint32        image_ID,
  332.       GimpDrawable *drawable,
  333.       gboolean      preview_mode)
  334. {
  335.   GimpPixelRgn srcPR, destPR;
  336.   gint width, height, bytes;
  337.   gint x_offset, y_offset;
  338.   guchar *dest;
  339.   gint x, y;
  340.   gboolean alpha;
  341.   gboolean blend;
  342.   guchar hcolor[4];
  343.   guchar vcolor[4];
  344.   guchar icolor[4];
  345.   guchar *cmap;
  346.   gint ncolors;
  347.   
  348.   if (preview_mode) 
  349.     {
  350.       memcpy (hcolor, grid_cfg.hcolor, 4);
  351.       memcpy (vcolor, grid_cfg.vcolor, 4);
  352.       memcpy (icolor, grid_cfg.icolor, 4);
  353.       blend = TRUE;
  354.     } 
  355.   else 
  356.     {
  357.       switch (gimp_image_base_type (image_ID))
  358.     {
  359.     case GIMP_RGB:
  360.       memcpy (hcolor, grid_cfg.hcolor, 4);
  361.       memcpy (vcolor, grid_cfg.vcolor, 4);
  362.       memcpy (icolor, grid_cfg.icolor, 4);
  363.       blend = TRUE;
  364.       break;
  365.  
  366.     case GIMP_GRAY:
  367.       hcolor[0] = INTENSITY (grid_cfg.hcolor[0], grid_cfg.hcolor[1], grid_cfg.hcolor[2]);
  368.       hcolor[3] = grid_cfg.hcolor[3];
  369.       vcolor[0] = INTENSITY (grid_cfg.vcolor[0], grid_cfg.vcolor[1], grid_cfg.vcolor[2]);
  370.       vcolor[3] = grid_cfg.vcolor[3];
  371.       icolor[0] = INTENSITY (grid_cfg.icolor[0], grid_cfg.icolor[1], grid_cfg.icolor[2]);
  372.       icolor[3] = grid_cfg.icolor[3];
  373.       blend = TRUE;
  374.       break;
  375.  
  376.     case GIMP_INDEXED:
  377.       cmap = gimp_image_get_cmap (image_ID, &ncolors);
  378.       hcolor[0] = best_cmap_match (cmap, ncolors, grid_cfg.hcolor);
  379.       hcolor[3] = grid_cfg.hcolor[3];
  380.       vcolor[0] = best_cmap_match (cmap, ncolors, grid_cfg.vcolor);
  381.       vcolor[3] = grid_cfg.vcolor[3];
  382.       icolor[0] = best_cmap_match (cmap, ncolors, grid_cfg.icolor);
  383.       icolor[3] = grid_cfg.icolor[3];
  384.       g_free (cmap);
  385.       blend = FALSE;
  386.       break;
  387.  
  388.     default:
  389.       g_assert_not_reached ();
  390.       blend = FALSE;
  391.     }
  392.     }
  393.   
  394.   if (preview_mode) 
  395.     { 
  396.       sx1 = sy1 = 0;
  397.       sx2 = GTK_PREVIEW (preview)->buffer_width;
  398.       sy2 = GTK_PREVIEW (preview)->buffer_height;
  399.       width  = sx2;
  400.       height = sy2;
  401.       alpha  = 0;
  402.       bytes  = GTK_PREVIEW (preview)->bpp;
  403.     } 
  404.   else 
  405.     {
  406.       /* Get the input area. This is the bounding box of the selection in
  407.        *  the image (or the entire image if there is no selection). 
  408.        */
  409.       gimp_drawable_mask_bounds (drawable->id, &sx1, &sy1, &sx2, &sy2);
  410.       width  = gimp_drawable_width (drawable->id);
  411.       height = gimp_drawable_height (drawable->id);
  412.       alpha  = gimp_drawable_has_alpha (drawable->id);
  413.       bytes  = drawable->bpp;
  414.       
  415.       /*  initialize the pixel regions  */
  416.       gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
  417.       gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE);
  418.     }
  419.  
  420.   dest = g_malloc (width * bytes);
  421.   for (y = sy1; y < sy2; y++)
  422.     {
  423.       if (preview_mode) 
  424.     memcpy (dest, 
  425.         preview_bits + (GTK_PREVIEW (preview)->rowstride * y),
  426.         GTK_PREVIEW (preview)->rowstride);
  427.       else 
  428.     gimp_pixel_rgn_get_row (&srcPR, dest, sx1, y, (sx2 - sx1));
  429.  
  430.       y_offset = y - grid_cfg.voffset;
  431.       while (y_offset < 0)
  432.     y_offset += grid_cfg.vspace;
  433.  
  434.       if ((y_offset + (grid_cfg.vwidth / 2)) % grid_cfg.vspace < grid_cfg.vwidth)
  435.     {
  436.       for (x = sx1; x < sx2; x++)
  437.         {
  438.           pix_composite (&dest[(x-sx1) * bytes], hcolor, bytes, blend, alpha);
  439.         }
  440.     }
  441.  
  442.       if ((y_offset + (grid_cfg.iwidth / 2)) % grid_cfg.vspace < grid_cfg.iwidth)
  443.         {
  444.       for (x = sx1; x < sx2; x++)
  445.         {
  446.           x_offset = grid_cfg.hspace + x - grid_cfg.hoffset;
  447.           while (x_offset < 0)
  448.         x_offset += grid_cfg.hspace;
  449.  
  450.               if ((x_offset % grid_cfg.hspace >= grid_cfg.ispace
  451.            && 
  452.            x_offset % grid_cfg.hspace < grid_cfg.ioffset) 
  453.           || 
  454.           (grid_cfg.hspace - (x_offset % grid_cfg.hspace) >= grid_cfg.ispace
  455.            && 
  456.            grid_cfg.hspace - (x_offset % grid_cfg.hspace) < grid_cfg.ioffset))
  457.         {
  458.           pix_composite (&dest[(x-sx1) * bytes], icolor, bytes, blend, alpha);
  459.                 }
  460.         }
  461.         }
  462.  
  463.       for (x = sx1; x < sx2; x++)
  464.         {
  465.       x_offset = grid_cfg.hspace + x - grid_cfg.hoffset;
  466.       while (x_offset < 0)
  467.         x_offset += grid_cfg.hspace;
  468.  
  469.           if ((x_offset + (grid_cfg.hwidth / 2)) % grid_cfg.hspace < grid_cfg.hwidth)
  470.             {
  471.           pix_composite (&dest[(x-sx1) * bytes], vcolor, bytes, blend, alpha);
  472.             }
  473.  
  474.           if ((x_offset + (grid_cfg.iwidth / 2)) % grid_cfg.hspace < grid_cfg.iwidth
  475.           && 
  476.           ((y_offset % grid_cfg.vspace >= grid_cfg.ispace
  477.         && 
  478.         y_offset % grid_cfg.vspace < grid_cfg.ioffset)
  479.            || 
  480.            (grid_cfg.vspace - (y_offset % grid_cfg.vspace) >= grid_cfg.ispace
  481.         && 
  482.         grid_cfg.vspace - (y_offset % grid_cfg.vspace) < grid_cfg.ioffset)))
  483.             {
  484.           pix_composite (&dest[(x-sx1) * bytes], icolor, bytes, blend, alpha);
  485.             }
  486.         }
  487.       if (preview_mode) 
  488.     {
  489.       memcpy (GTK_PREVIEW (preview)->buffer + (GTK_PREVIEW (preview)->rowstride * y),
  490.           dest,
  491.           GTK_PREVIEW (preview)->rowstride);
  492.     } 
  493.       else 
  494.     {
  495.       gimp_pixel_rgn_set_row (&destPR, dest, sx1, y, (sx2-sx1) );
  496.       gimp_progress_update ((double) y / (double) (sy2 - sy1));
  497.     }
  498.     } 
  499.   g_free (dest);
  500.  
  501.   /*  update the timred region  */
  502.   if (preview_mode) 
  503.     {
  504.       gtk_widget_queue_draw (preview);
  505.     } 
  506.   else 
  507.     {
  508.       gimp_drawable_flush (drawable);
  509.       gimp_drawable_merge_shadow (drawable->id, TRUE);
  510.       gimp_drawable_update (drawable->id, sx1, sy1, sx2 - sx1, sy2 - sy1);
  511.     }
  512. }
  513.  
  514.  
  515. /***************************************************
  516.  * GUI stuff
  517.  */
  518.  
  519. static void
  520. ok_callback (GtkWidget *widget, 
  521.          gpointer   data)
  522. {
  523.   GtkWidget *entry;
  524.  
  525.   run_flag = TRUE;
  526.  
  527.   entry = gtk_object_get_data (GTK_OBJECT (data), "width");
  528.   grid_cfg.hwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  529.   grid_cfg.vwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  530.   grid_cfg.iwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  531.   
  532.   entry = gtk_object_get_data (GTK_OBJECT (data), "space");
  533.   grid_cfg.hspace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  534.   grid_cfg.vspace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  535.   grid_cfg.ispace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  536.  
  537.   entry = gtk_object_get_data (GTK_OBJECT (data), "offset");
  538.   grid_cfg.hoffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  539.   grid_cfg.voffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  540.   grid_cfg.ioffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  541.  
  542.   gtk_widget_destroy (GTK_WIDGET (data));
  543. }
  544.  
  545.  
  546. static void
  547. entry_callback (GtkWidget *widget, 
  548.         gpointer   data)
  549. {
  550.   static gdouble x = -1.0;
  551.   static gdouble y = -1.0;
  552.   gdouble new_x;
  553.   gdouble new_y;
  554.  
  555.   new_x = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
  556.   new_y = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
  557.  
  558.   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (data)))
  559.     {
  560.       if (new_x != x)
  561.     {
  562.       y = new_y = x = new_x;
  563.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1, y);
  564.     }
  565.       if (new_y != y)
  566.     {
  567.       x = new_x = y = new_y;
  568.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0, x);
  569.     }
  570.     }
  571.   else
  572.     {
  573.       if (new_x != x)
  574.     x = new_x;
  575.       if (new_y != y)
  576.     y = new_y;
  577.     }     
  578. }
  579.  
  580.  
  581. static void
  582. color_callback (GtkWidget *widget, 
  583.         gpointer   data)
  584. {
  585.   gint i;
  586.  
  587.   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (data)))
  588.     {
  589.       if (widget == hcolor_button)
  590.     {
  591.       for (i = 0; i < 4; i++)
  592.         grid_cfg.vcolor[i] = grid_cfg.hcolor[i];
  593.       gimp_color_button_update (GIMP_COLOR_BUTTON (vcolor_button));
  594.     }
  595.       else
  596.     {
  597.       for (i = 0; i < 4; i++)
  598.         grid_cfg.hcolor[i] = grid_cfg.vcolor[i];
  599.       gimp_color_button_update (GIMP_COLOR_BUTTON (hcolor_button));
  600.     }    
  601.     }
  602. }
  603.  
  604.  
  605. static void
  606. update_preview_callback (GtkWidget *widget, 
  607.              gpointer   data)
  608. {
  609.   GimpDrawable *drawable;
  610.   GtkWidget    *entry;
  611.  
  612.   drawable = gtk_object_get_data (GTK_OBJECT (widget), "drawable");
  613.  
  614.   entry = gtk_object_get_data (GTK_OBJECT (widget), "width");
  615.   grid_cfg.hwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  616.   grid_cfg.vwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  617.   grid_cfg.iwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  618.  
  619.   entry = gtk_object_get_data (GTK_OBJECT (widget), "space");
  620.   grid_cfg.hspace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  621.   grid_cfg.vspace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  622.   grid_cfg.ispace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  623.  
  624.   entry = gtk_object_get_data (GTK_OBJECT (widget), "offset");
  625.   grid_cfg.hoffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  626.   grid_cfg.voffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  627.   grid_cfg.ioffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  628.  
  629.   doit (0, drawable, TRUE); /* we can set image_ID = 0 'cause we dont use it */
  630. }
  631.  
  632. static gint
  633. dialog (gint32        image_ID,
  634.     GimpDrawable *drawable)
  635. {
  636.   GtkWidget *dlg;
  637.   GtkWidget *main_hbox;
  638.   GtkWidget *main_vbox;
  639.   GtkWidget *frame;
  640.   GtkWidget *abox;
  641.  
  642.   GtkWidget *button;
  643.   GtkWidget *hbox;
  644.   GtkWidget *width;
  645.   GtkWidget *space;
  646.   GtkWidget *offset;
  647.   GtkWidget *chain_button;
  648.   GtkWidget *table;
  649.   GtkWidget *align;
  650.   GimpUnit   unit;
  651.   gdouble    xres;
  652.   gdouble    yres;
  653.  
  654.   gimp_ui_init ("grid", TRUE);
  655.  
  656.   dlg = gimp_dialog_new (_("Grid"), "grid",
  657.              gimp_standard_help_func, "filters/grid.html",
  658.              GTK_WIN_POS_MOUSE,
  659.              FALSE, TRUE, FALSE,
  660.  
  661.              _("OK"), ok_callback,
  662.              NULL, NULL, NULL, TRUE, FALSE,
  663.              _("Cancel"), gtk_widget_destroy,
  664.              NULL, 1, NULL, FALSE, TRUE,
  665.  
  666.              NULL);
  667.  
  668.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  669.               GTK_SIGNAL_FUNC (gtk_main_quit),
  670.               NULL);
  671.  
  672.   /*  Get the image resolution and unit  */
  673.   gimp_image_get_resolution (image_ID, &xres, &yres);
  674.   unit = gimp_image_get_unit (image_ID);
  675.  
  676.   /* main hbox: [   ][       ] */
  677.   main_hbox = gtk_hbox_new (FALSE, 2);
  678.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), main_hbox, FALSE, FALSE, 4);
  679.   /* hbox created and packed into the dialog */
  680.  
  681.   /* make a nice frame */
  682.   frame = gtk_frame_new (_("Preview"));
  683.   gtk_container_set_border_width (GTK_CONTAINER (frame), 4);
  684.   gtk_box_pack_start (GTK_BOX (main_hbox), frame, FALSE, FALSE, 0);
  685.   gtk_widget_show (frame);
  686.  
  687.   hbox = gtk_vbox_new (FALSE, 2);
  688.   gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
  689.   gtk_container_add (GTK_CONTAINER (frame), hbox);
  690.   gtk_widget_show (hbox);
  691.  
  692.   /* row 1 */
  693.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  694.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  695.   gtk_box_pack_start (GTK_BOX (hbox), abox, TRUE, TRUE, 0);
  696.   gtk_widget_show (abox);
  697.   frame = gtk_frame_new (NULL);
  698.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  699.   gtk_container_add (GTK_CONTAINER (abox), frame);
  700.   gtk_widget_show (frame);
  701.   preview = preview_widget (image_ID, drawable); /* we are here */
  702.   gtk_container_add (GTK_CONTAINER (frame), preview);
  703.   doit (image_ID, drawable, TRUE); /* render preview */
  704.   gtk_widget_show (preview);
  705.  
  706.   /* row 2 */
  707.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  708.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  709.   gtk_container_add (GTK_CONTAINER (hbox), abox);
  710.   gtk_widget_show (abox);
  711.   button = gtk_button_new_with_label (_("Update Preview"));
  712.   gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
  713.   gtk_object_set_data (GTK_OBJECT (button), "drawable", drawable);
  714.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  715.               GTK_SIGNAL_FUNC (update_preview_callback),
  716.               NULL);
  717.   gtk_container_add (GTK_CONTAINER (abox), button);
  718.   gtk_widget_show (button);
  719.   /* left side of the UI is done */
  720.  
  721.   /* right side */
  722.   frame = gtk_frame_new (_("Parameter Settings"));
  723.   gtk_container_set_border_width (GTK_CONTAINER (frame), 4);
  724.   gtk_box_pack_start (GTK_BOX (main_hbox), frame, FALSE, FALSE, 0);
  725.   gtk_widget_show (frame);
  726.  
  727.   main_vbox = gtk_vbox_new (FALSE, 4);
  728.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  729.   gtk_container_add (GTK_CONTAINER (frame), main_vbox);
  730.   gtk_widget_show (main_vbox);
  731.   
  732.   /*  The width entries  */
  733.   width = gimp_size_entry_new (3,                            /*  number_of_fields  */ 
  734.                    unit,                         /*  unit              */
  735.                    "%a",                         /*  unit_format       */
  736.                    TRUE,                         /*  menu_show_pixels  */
  737.                    TRUE,                         /*  menu_show_percent */
  738.                    FALSE,                        /*  show_refval       */
  739.                    SPIN_BUTTON_WIDTH,            /*  spinbutton_usize  */
  740.                    GIMP_SIZE_ENTRY_UPDATE_SIZE); /*  update_policy     */
  741.   gtk_object_set_data (GTK_OBJECT (button), "width", width);
  742.  
  743.   /*  set the unit back to pixels, since most times we will want pixels */
  744.   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (width), GIMP_UNIT_PIXEL);
  745.  
  746.   /*  set the resolution to the image resolution  */
  747.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 0, xres, TRUE);
  748.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 1, yres, TRUE);
  749.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 2, xres, TRUE);
  750.  
  751.   /*  set the size (in pixels) that will be treated as 0% and 100%  */
  752.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 0, 0.0, (gdouble)(drawable->width));
  753.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 1, 0.0, (gdouble)(drawable->height));
  754.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 2, 0.0, (gdouble)(drawable->width));
  755.  
  756.   /*  set upper and lower limits (in pixels)  */
  757.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 0, 0.0, 
  758.                      (gdouble)(drawable->width));
  759.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 1, 0.0, 
  760.                      (gdouble)(drawable->height));
  761.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 2, 0.0, 
  762.                      (gdouble)(MAX (drawable->width, drawable->height)));
  763.   gtk_table_set_col_spacing (GTK_TABLE (width), 2, 12);
  764.   gtk_table_set_col_spacing (GTK_TABLE (width), 3, 12);
  765.  
  766.   /*  initialize the values  */
  767.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 0, (gdouble)grid_cfg.hwidth);
  768.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 1, (gdouble)grid_cfg.vwidth);
  769.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 2, (gdouble)grid_cfg.iwidth);
  770.  
  771.   /*  attach labels  */
  772.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Horizontal"), 0, 1, 0.0);
  773.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Vertical"), 0, 2, 0.0);
  774.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Intersection"), 0, 3, 0.0);
  775.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Width: "), 1, 0, 0.0); 
  776.  
  777.   /*  put a chain_button under the size_entries  */
  778.   chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
  779.   if (grid_cfg.hwidth == grid_cfg.vwidth)
  780.     gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
  781.   gtk_table_attach_defaults (GTK_TABLE (width), chain_button, 1, 3, 2, 3);
  782.   gtk_widget_show (chain_button);
  783.  
  784.   /*  connect to the 'value_changed' signal because we have to take care 
  785.       of keeping the entries in sync when the chainbutton is active        */
  786.   gtk_signal_connect (GTK_OBJECT (width), "value_changed", 
  787.               (GtkSignalFunc) entry_callback, chain_button);
  788.  
  789.   abox = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
  790.   gtk_container_add (GTK_CONTAINER (abox), width);
  791.   gtk_box_pack_start (GTK_BOX (main_vbox), abox, FALSE, FALSE, 4);
  792.   gtk_widget_show (width);
  793.   gtk_widget_show (abox);
  794.  
  795.   /*  The spacing entries  */
  796.   space = gimp_size_entry_new (3,                            /*  number_of_fields  */ 
  797.                    unit,                         /*  unit              */
  798.                    "%a",                         /*  unit_format       */
  799.                    TRUE,                         /*  menu_show_pixels  */
  800.                    TRUE,                         /*  menu_show_percent */
  801.                    FALSE,                        /*  show_refval       */
  802.                    SPIN_BUTTON_WIDTH,            /*  spinbutton_usize  */
  803.                    GIMP_SIZE_ENTRY_UPDATE_SIZE); /*  update_policy     */
  804.   gtk_object_set_data (GTK_OBJECT (button), "space", space);
  805.   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (space), GIMP_UNIT_PIXEL);
  806.  
  807.   /*  set the resolution to the image resolution  */
  808.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 0, xres, TRUE);
  809.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 1, yres, TRUE);
  810.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 2, xres, TRUE);
  811.  
  812.   /*  set the size (in pixels) that will be treated as 0% and 100%  */
  813.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 0, 0.0, (gdouble)(drawable->width));
  814.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 1, 0.0, (gdouble)(drawable->height));
  815.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 2, 0.0, (gdouble)(drawable->width));
  816.  
  817.   /*  set upper and lower limits (in pixels)  */
  818.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 0, 1.0, 
  819.                      (gdouble)(drawable->width));
  820.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 1, 1.0, 
  821.                      (gdouble)(drawable->height));
  822.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 2, 0.0, 
  823.                      (gdouble)(MAX (drawable->width, drawable->height)));
  824.   gtk_table_set_col_spacing (GTK_TABLE (space), 2, 12);
  825.   gtk_table_set_col_spacing (GTK_TABLE (space), 3, 12);
  826.  
  827.   /*  initialize the values  */
  828.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 0, (gdouble)grid_cfg.hspace);
  829.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 1, (gdouble)grid_cfg.vspace);
  830.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 2, (gdouble)grid_cfg.ispace);
  831.   /*  attach labels  */
  832.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (space), _("Spacing: "), 1, 0, 0.0); 
  833.  
  834.   /*  put a chain_button under the spacing_entries  */
  835.   chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
  836.   if (grid_cfg.hspace == grid_cfg.vspace)
  837.     gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
  838.   gtk_table_attach_defaults (GTK_TABLE (space), chain_button, 1, 3, 2, 3);
  839.   gtk_widget_show (chain_button);
  840.  
  841.   /*  connect to the 'value_changed' and "unit_changed" signals because we have to 
  842.       take care of keeping the entries in sync when the chainbutton is active        */
  843.   gtk_signal_connect (GTK_OBJECT (space), "value_changed", 
  844.               (GtkSignalFunc) entry_callback, chain_button);
  845.   gtk_signal_connect (GTK_OBJECT (space), "unit_changed", 
  846.               (GtkSignalFunc) entry_callback, chain_button);
  847.  
  848.   abox = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
  849.   gtk_container_add (GTK_CONTAINER (abox), space);
  850.   gtk_box_pack_start (GTK_BOX (main_vbox), abox, FALSE, FALSE, 4);
  851.   gtk_widget_show (space);
  852.   gtk_widget_show (abox);
  853.  
  854.   /*  The offset entries  */
  855.   offset = gimp_size_entry_new (3,                            /*  number_of_fields  */ 
  856.                 unit,                         /*  unit              */
  857.                 "%a",                         /*  unit_format       */
  858.                 TRUE,                         /*  menu_show_pixels  */
  859.                 TRUE,                         /*  menu_show_percent */
  860.                 FALSE,                        /*  show_refval       */
  861.                 SPIN_BUTTON_WIDTH,            /*  spinbutton_usize  */
  862.                 GIMP_SIZE_ENTRY_UPDATE_SIZE); /*  update_policy     */
  863.   gtk_object_set_data (GTK_OBJECT (button), "offset", offset);
  864.   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (offset), GIMP_UNIT_PIXEL);
  865.  
  866.   /*  set the resolution to the image resolution  */
  867.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 0, xres, TRUE);
  868.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 1, yres, TRUE);
  869.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 2, xres, TRUE);
  870.  
  871.   /*  set the size (in pixels) that will be treated as 0% and 100%  */
  872.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 0, 0.0, (gdouble)(drawable->width));
  873.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 1, 0.0, (gdouble)(drawable->height));
  874.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 2, 0.0, (gdouble)(drawable->width));
  875.  
  876.   /*  set upper and lower limits (in pixels)  */
  877.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 0, 0.0, 
  878.                      (gdouble)(drawable->width));
  879.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 1, 0.0, 
  880.                      (gdouble)(drawable->height));
  881.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 2, 0.0, 
  882.                      (gdouble)(MAX (drawable->width, drawable->height)));
  883.   gtk_table_set_col_spacing (GTK_TABLE (offset), 2, 12);
  884.   gtk_table_set_col_spacing (GTK_TABLE (offset), 3, 12);
  885.  
  886.   /*  initialize the values  */
  887.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 0, (gdouble)grid_cfg.hoffset);
  888.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 1, (gdouble)grid_cfg.voffset);
  889.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 2, (gdouble)grid_cfg.ioffset);
  890.  
  891.   /*  attach labels  */
  892.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (offset), _("Offset: "), 1, 0, 0.0); 
  893.  
  894.   /*  this is a weird hack: we put a table into the offset table  */
  895.   table = gtk_table_new (3, 3, FALSE);
  896.   gtk_table_attach_defaults (GTK_TABLE (offset), table, 1, 4, 2, 3);
  897.   gtk_table_set_row_spacing (GTK_TABLE (table), 0, 10);
  898.   gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
  899.  
  900.   /*  put a chain_button under the offset_entries  */
  901.   chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
  902.   if (grid_cfg.hoffset == grid_cfg.voffset)
  903.     gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
  904.   gtk_table_attach_defaults (GTK_TABLE (table), chain_button, 0, 2, 0, 1);
  905.   gtk_widget_show (chain_button);
  906.  
  907.   /*  connect to the 'value_changed' and "unit_changed" signals because we have to 
  908.       take care of keeping the entries in sync when the chainbutton is active        */
  909.   gtk_signal_connect (GTK_OBJECT (offset), "value_changed", 
  910.               (GtkSignalFunc) entry_callback, chain_button);
  911.   gtk_signal_connect (GTK_OBJECT (offset), "unit_changed",
  912.               (GtkSignalFunc) entry_callback, chain_button);
  913.  
  914.   /*  put a chain_button under the color_buttons  */
  915.   chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
  916.   if ((guint32)(*grid_cfg.hcolor) == (guint32)(*grid_cfg.vcolor))
  917.     gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
  918.   gtk_table_attach_defaults (GTK_TABLE (table), chain_button, 0, 2, 2, 3);
  919.   gtk_widget_show (chain_button);
  920.  
  921.   /*  attach color selectors  */
  922.   hcolor_button = gimp_color_button_new (_("Horizontal Color"), COLOR_BUTTON_WIDTH, 16, 
  923.                      grid_cfg.hcolor, 4);
  924.   gtk_signal_connect (GTK_OBJECT (hcolor_button), "color_changed", 
  925.               (GtkSignalFunc) color_callback, chain_button);
  926.   align = gtk_alignment_new (0.0, 0.5, 0, 0);
  927.   gtk_container_add (GTK_CONTAINER (align),  hcolor_button);
  928.   gtk_table_attach_defaults (GTK_TABLE (table), align, 0, 1, 1, 2);
  929.   gtk_widget_show (hcolor_button);
  930.   gtk_widget_show (align);
  931.  
  932.   vcolor_button = gimp_color_button_new (_("Vertical Color"), COLOR_BUTTON_WIDTH, 16, 
  933.                      grid_cfg.vcolor, 4);
  934.   gtk_signal_connect (GTK_OBJECT (vcolor_button), "color_changed", 
  935.               (GtkSignalFunc) color_callback, chain_button);  
  936.   align = gtk_alignment_new (0.0, 0.5, 0, 0);
  937.   gtk_container_add (GTK_CONTAINER (align), vcolor_button);
  938.   gtk_table_attach_defaults (GTK_TABLE (table), align, 1, 2, 1, 2);
  939.   gtk_widget_show (vcolor_button);
  940.   gtk_widget_show (align);
  941.  
  942.   button = gimp_color_button_new (_("Intersection Color"), COLOR_BUTTON_WIDTH, 16, 
  943.                   grid_cfg.icolor, 4);
  944.   align = gtk_alignment_new (0.0, 0.5, 0, 0);
  945.   gtk_container_add (GTK_CONTAINER (align), button);
  946.   gtk_table_attach_defaults (GTK_TABLE (table), align, 2, 3, 1, 2);
  947.   gtk_widget_show (button);
  948.   gtk_widget_show (align);
  949.   gtk_widget_show (table);
  950.  
  951.   abox = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
  952.   gtk_container_add (GTK_CONTAINER (abox), offset);
  953.   gtk_box_pack_start (GTK_BOX (main_vbox), abox, FALSE, FALSE, 4);
  954.   gtk_widget_show (offset);
  955.   gtk_widget_show (abox);
  956.  
  957.   gtk_widget_show (main_hbox); 
  958.   gtk_widget_show (dlg);
  959.  
  960.   gtk_object_set_data (GTK_OBJECT (dlg), "width",  width);
  961.   gtk_object_set_data (GTK_OBJECT (dlg), "space",  space);  
  962.   gtk_object_set_data (GTK_OBJECT (dlg), "offset", offset);  
  963.  
  964.   gtk_main ();
  965.   gdk_flush ();
  966.  
  967.   return run_flag;
  968. }
  969.  
  970. static GtkWidget *
  971. preview_widget (gint32        image_ID,
  972.                 GimpDrawable *drawable)
  973. {
  974.   gint       size;
  975.   GtkWidget *preview;
  976.  
  977.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  978.   fill_preview (preview, image_ID, drawable);
  979.   size = 
  980.     GTK_PREVIEW (preview)->rowstride * GTK_PREVIEW (preview)->buffer_height;
  981.   preview_bits = g_malloc (size);
  982.   memcpy (preview_bits, GTK_PREVIEW (preview)->buffer, size);
  983.   
  984.   return preview;
  985. }
  986.  
  987. static void
  988. fill_preview (GtkWidget    *widget,
  989.               gint32        image_ID,
  990.           GimpDrawable *drawable)
  991. {
  992.   GimpPixelRgn  srcPR;
  993.   gint          width;
  994.   gint          height;
  995.   gint          x1, x2, y1, y2;
  996.   gint          bpp;
  997.   gint          x, y;
  998.   guchar       *src;
  999.   gdouble       r, g, b, a;
  1000.   gdouble       c0, c1;
  1001.   guchar       *p0, *p1;
  1002.   guchar       *even, *odd;
  1003.   guchar       *cmap;
  1004.   gint          ncolors;
  1005.   
  1006.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  1007.  
  1008.   if (x2 - x1 > PREVIEW_SIZE)
  1009.     x2 = x1 + PREVIEW_SIZE;
  1010.   
  1011.   if (y2 - y1 > PREVIEW_SIZE)
  1012.     y2 = y1 + PREVIEW_SIZE;
  1013.   
  1014.   width  = x2 - x1;
  1015.   height = y2 - y1;
  1016.   bpp    = gimp_drawable_bpp (drawable->id);
  1017.   
  1018.   if (width < 1 || height < 1)
  1019.     return;
  1020.  
  1021.   gtk_preview_size (GTK_PREVIEW (widget), width, height);
  1022.  
  1023.   gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, x2, y2, FALSE, FALSE);
  1024.  
  1025.   even = g_malloc (width * 3);
  1026.   odd  = g_malloc (width * 3);
  1027.   src  = g_malloc (width * bpp);
  1028.  
  1029.   if (gimp_drawable_is_indexed (drawable->id))
  1030.     cmap = gimp_image_get_cmap (image_ID, &ncolors);
  1031.   else
  1032.     cmap = NULL;
  1033.  
  1034.   for (y = 0; y < height; y++)
  1035.     {
  1036.       gimp_pixel_rgn_get_row (&srcPR, src, x1, y + y1, width);
  1037.       p0 = even;
  1038.       p1 = odd;
  1039.       
  1040.       for (x = 0; x < width; x++) 
  1041.     {
  1042.       if (bpp == 4)
  1043.         {
  1044.           r = ((gdouble)src[x*4+0]) / 255.0;
  1045.           g = ((gdouble)src[x*4+1]) / 255.0;
  1046.           b = ((gdouble)src[x*4+2]) / 255.0;
  1047.           a = ((gdouble)src[x*4+3]) / 255.0;
  1048.         }
  1049.       else if (bpp == 3)
  1050.         {
  1051.           r = ((gdouble)src[x*3+0]) / 255.0;
  1052.           g = ((gdouble)src[x*3+1]) / 255.0;
  1053.           b = ((gdouble)src[x*3+2]) / 255.0;
  1054.           a = 1.0;
  1055.         }
  1056.       else
  1057.         {
  1058.               if (cmap)
  1059.                 {
  1060.                   gint index = MIN (src[x*bpp], ncolors - 1);
  1061.  
  1062.                   r = ((gdouble)cmap[index * 3 + 0]) / 255.0;
  1063.                   g = ((gdouble)cmap[index * 3 + 1]) / 255.0;
  1064.                   b = ((gdouble)cmap[index * 3 + 2]) / 255.0;
  1065.                 }
  1066.               else
  1067.                 {
  1068.                   r = ((gdouble)src[x*bpp+0]) / 255.0;
  1069.                   g = b = r;
  1070.                 }
  1071.  
  1072.           if (bpp == 2)
  1073.         a = ((gdouble)src[x*bpp+1]) / 255.0;
  1074.           else
  1075.         a = 1.0;
  1076.         }
  1077.     
  1078.     if ((x / GIMP_CHECK_SIZE) & 1) 
  1079.       {
  1080.         c0 = GIMP_CHECK_LIGHT;
  1081.         c1 = GIMP_CHECK_DARK;
  1082.       } 
  1083.     else 
  1084.       {
  1085.         c0 = GIMP_CHECK_DARK;
  1086.         c1 = GIMP_CHECK_LIGHT;
  1087.       }
  1088.     
  1089.     *p0++ = (c0 + (r - c0) * a) * 255.0;
  1090.     *p0++ = (c0 + (g - c0) * a) * 255.0;
  1091.     *p0++ = (c0 + (b - c0) * a) * 255.0;
  1092.     
  1093.     *p1++ = (c1 + (r - c1) * a) * 255.0;
  1094.     *p1++ = (c1 + (g - c1) * a) * 255.0;
  1095.     *p1++ = (c1 + (b - c1) * a) * 255.0;
  1096.     
  1097.       } /* for */
  1098.       
  1099.       if ((y / GIMP_CHECK_SIZE) & 1)
  1100.     gtk_preview_draw_row (GTK_PREVIEW (widget), (guchar *)odd,  0, y, width);
  1101.       else
  1102.     gtk_preview_draw_row (GTK_PREVIEW (widget), (guchar *)even, 0, y, width);
  1103.     }
  1104.  
  1105.   g_free (even);
  1106.   g_free (odd);
  1107.   g_free (src);
  1108.  
  1109.   if (cmap)
  1110.     g_free (cmap);
  1111. }
  1112.